package com.csw.android.dev_utils.utils

import android.Manifest
import android.annotation.SuppressLint
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothManager
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import androidx.core.content.ContextCompat
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import com.csw.android.dev_utils.SDK
import com.csw.android.dev_utils.ex.ContextEx.isAllPermissionGranted
import com.csw.android.dev_utils.handler.MainHandler
import java.util.concurrent.CopyOnWriteArrayList
import java.util.regex.Pattern

/**
 * Android 7.0以后，针对APP的蓝牙扫描做了限制，30秒内最多扫描5次，超出的次数将不再扫描到任何设备
 * 开发此扫描工具类，用于辅助APP蓝牙扫描控制
 *
 * 由于蓝牙设备处于广播中时，APP扫描过程中每次解析电磁波都会返回该设备，目前采取的策略是：
 * 中间做一层扫描代理，每次扫描最少持续6秒才会停止，这样可以避免因超出系统限制导致的扫描不到设备
 * （通过其他途径进行蓝牙扫描不再此记录之中，所以仍有可能导致bug）。
 * 代理层将每次扫描到的设备转发给外部监听器，由其进行处理。
 */
object BluetoothScanUtils {
    /** 一次扫描的最短持续时间 */
    private const val MIN_SCAN_TIME = 6000L
    private val bluetoothManager =
        SDK.getApplication().getSystemService(Context.BLUETOOTH_SERVICE) as? BluetoothManager
    private val bluetoothAdapter = bluetoothManager?.adapter

    /** 代理监听，负责分发到所有激活的外部监听器 */
    private val proxyListener = BluetoothAdapter.LeScanCallback { device, rssi, scanRecord ->
        scanListeners.forEach {
            if (it.isActive()) {
                it.onLeScan(device, rssi, scanRecord)
            }
        }
    }

    /** 外部监听器集合 */
    private val scanListeners = HashSet<BluetoothScanListener>()

    /** 扫描开始时间 */
    private var scanStartTime = 0L
    private val mainHandler = MainHandler()
    private val stopScanTask = Runnable {
        stopScanWithCheck()
    }

    /** 当前处于扫描中 */
    private var scanning = false
        @SuppressLint("MissingPermission") set(value) {
            if (field != value) {
                field = value
                if (field) {
                    field = bluetoothAdapter?.startLeScan(proxyListener) == true
                    LogUtils.i("BluetoothScanUtils", "startLeScan:${field}")
                    if (field) {
                        scanStartTime = System.currentTimeMillis()
                        mainHandler.removeCallbacks(stopScanTask)
                        mainHandler.postDelayed(stopScanTask, MIN_SCAN_TIME)
                    }
                } else {
                    bluetoothAdapter?.stopLeScan(proxyListener)
                    LogUtils.i("BluetoothScanUtils", "stopLeScan")
                }
            }
        }

    /**
     * 开始蓝牙扫描，需要蓝牙管理、位置、蓝牙扫描等权限
     * 若没有权限，则回调[listener].onScanStarted(false)
     */
    fun startScan(listener: BluetoothScanListener?) {
        val permissions = ArrayList<String>()
        permissions.add(Manifest.permission.BLUETOOTH)
        permissions.add(Manifest.permission.BLUETOOTH_ADMIN)
        permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION)
        permissions.add(Manifest.permission.ACCESS_FINE_LOCATION)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            permissions.add(Manifest.permission.BLUETOOTH_SCAN)
        }
        if (SDK.getApplication().isAllPermissionGranted(*(permissions.toTypedArray()))) {
            if (bluetoothAdapter?.isEnabled == true) {
                scanning = true
            }
        }
        if (scanning) {
            if (listener != null && scanListeners.add(listener)) {
                listener.onScanStarted(scanning)
            }
        } else {
            listener?.onScanStarted(scanning)
        }
    }

    /** 停止蓝牙扫描 */
    fun stopScan(listener: BluetoothScanListener?) {
        listener?.let {
            if (scanListeners.remove(it)) {
                it.onScanFinished()
            }
        }
        stopScanWithCheck()
    }

    /**
     * 权限，是否不存在活跃的监听，最小扫描时间等因素检查都满足则可以关闭蓝牙扫描
     */
    private fun stopScanWithCheck() {
        val permissions = ArrayList<String>()
        permissions.add(Manifest.permission.BLUETOOTH)
        permissions.add(Manifest.permission.BLUETOOTH_ADMIN)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            permissions.add(Manifest.permission.BLUETOOTH_SCAN)
        }
        if (SDK.getApplication().isAllPermissionGranted(*(permissions.toTypedArray()))) {
            val hasAliveListener = scanListeners.find { it.isActive() } != null
            if (!hasAliveListener && System.currentTimeMillis() > (scanStartTime + MIN_SCAN_TIME)) {
                scanning = false
            }
        }
    }
}

interface BluetoothScanListener : BluetoothAdapter.LeScanCallback {

    /**
     * 扫描到设备，该方法每次扫描到设备都会回调，同个设备会回调多次，此方法在子线程执行
     */
    override fun onLeScan(device: BluetoothDevice?, rssi: Int, scanRecord: ByteArray?)

    /** 判断蓝牙扫描监听是否处于激活状态 */
    fun isActive(): Boolean

    /**
     * 开始扫描
     *
     * @param success true 开始扫描成功 else 开始扫描失败
     */
    fun onScanStarted(success: Boolean)

    /** 扫描结束 */
    fun onScanFinished()
}

open class CommonBluetoothScanListener : BluetoothScanListener {
    //region 生命周期监听 onResume开始扫描，onPause停止扫描---------------------------------------------
    /** 处于生命周期过程中 */
    private var inLifecycle = true
    private val lifecycleListener = object : DefaultLifecycleObserver {
        override fun onResume(owner: LifecycleOwner) {
            super.onResume(owner)
            inLifecycle = true
            BluetoothScanUtils.startScan(this@CommonBluetoothScanListener)
        }

        override fun onPause(owner: LifecycleOwner) {
            inLifecycle = false
            BluetoothScanUtils.stopScan(this@CommonBluetoothScanListener)
            super.onPause(owner)
        }
    }

    /**
     * 生命周期
     * 扫描任务将跟随生命周期（OnResume->OnPause）自动执行
     */
    var lifecycle: Lifecycle? = null
        set(value) {
            if (field != value) {
                field?.removeObserver(lifecycleListener)
                field = value
                inLifecycle = field == null
                field?.addObserver(lifecycleListener)
            }
        }

    /** 生命周期持有者[lifecycle] */
    var lifecycleOwner: LifecycleOwner? = null
        set(value) {
            field = value
            lifecycle = field?.lifecycle
        }

    //endregion

    //region 设置每次扫描的持续时间--------------------------------------------------------------------
    private var stopScanTask = Runnable {
        BluetoothScanUtils.stopScan(this@CommonBluetoothScanListener)
    }

    /** 开始扫描时间 */
    private var startScanTime: Long = 0

    /** 每次扫描时长（毫秒，<=0代表没有时长限制） */
    var scanDuration: Long = 0

    //endregion

    //region 设备过滤--------------------------------------------------------------------------------

    /** 设备名称匹配规则(获取蓝牙名称需要具备蓝牙连接权限，无权限则不过滤名称) */
    var deviceNamePattern: Pattern? = null

    //endregion

    private var mainHandler = MainHandler()
    val bleDevices = CopyOnWriteArrayList<BleDevice>()

    override fun isActive(): Boolean {
        if (!inLifecycle) {
            return false
        }
        if (startScanTime > 0 && scanDuration >= 0) {
            return (System.currentTimeMillis() - startScanTime) <= scanDuration
        }
        return true
    }

    @SuppressLint("MissingPermission")
    override fun onLeScan(device: BluetoothDevice?, rssi: Int, scanRecord: ByteArray?) {
        device ?: return
        //按名称过滤
        if (deviceNamePattern != null) {
            if (ContextCompat.checkSelfPermission(
                    SDK.getApplication(), Manifest.permission.BLUETOOTH_CONNECT
                ) == PackageManager.PERMISSION_GRANTED
            ) {
                val deviceName = device.name
                if (deviceName.isNullOrEmpty() || deviceNamePattern?.matcher(deviceName)
                        ?.matches() != true
                ) {
                    return
                }
            }
        }
        //重复设备过滤
        val existBleDevice = bleDevices.find { it.device == device }
        if (existBleDevice != null) {
            existBleDevice.rssi = rssi
            existBleDevice.scanRecord = scanRecord
            existBleDevice.lastDiscoverTime = System.currentTimeMillis()
            return
        }
        val newBleDevice = BleDevice(
            device, rssi, scanRecord, System.currentTimeMillis()
        )
        bleDevices.add(newBleDevice)
        mainHandler.post {
            if (isActive()) {
                onScanning(newBleDevice)
            }
        }
    }

    /**
     * 扫描到设备
     * 此方法在主线程执行
     * @param bleDevice 蓝牙设备，此方法接收的设备经过重复设备与名称过滤
     */
    open fun onScanning(bleDevice: BleDevice) {}

    override fun onScanStarted(success: Boolean) {
        bleDevices.clear()
        if (success) {
            //标记扫描开始时间
            startScanTime = System.currentTimeMillis()
            //设置了扫描时长，这里设置定时关闭扫描
            if (scanDuration > 0) {
                mainHandler.removeCallbacks(stopScanTask)
                mainHandler.postDelayed(stopScanTask, scanDuration)
            }
        }
    }

    override fun onScanFinished() {
        startScanTime = 0
        mainHandler.removeCallbacks(stopScanTask)
    }

    /**
     * 蓝牙设备
     * @param device 蓝牙设备对象
     * @param rssi 信号强度
     * @param scanRecord 广播数据
     * @param lastDiscoverTime 最后一次扫描到该设备的时间
     */
    class BleDevice(
        var device: BluetoothDevice,
        var rssi: Int,
        var scanRecord: ByteArray?,
        var lastDiscoverTime: Long
    )
}
